using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Security.Permissions;
using System.Threading;
using System.Web;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
using Microsoft.Web.Design.Server;
using SharePointPermission=Microsoft.SharePoint.Security.SharePointPermissionAttribute;

namespace MyDataSource
{
	[Designer("MyDesigner.MyDesignerImpl, MyDesigner, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6a885487f154dfcb"),
	//[ProxyDesigner("Microsoft.Web.Design.Remote.FpDataSourceProxyDesigner, Microsoft.Web.Design.Client, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"), 
	 PersistChildren(false), ParseChildren(ChildrenAsProperties = true),
	 SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true),
	 AspNetHostingPermissionAttribute(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal),
	 SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true),
	 AspNetHostingPermissionAttribute(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
	public class MyDataSource : DataSourceControl
	{
		private string m_ConnectionString;
	    private MyDSView m_View;

		public string ConnectionString
		{
			get { return m_ConnectionString; }
			set { m_ConnectionString = value; }
		}

	    private MyDSView View
	    {
	        get
	        {
                if(m_View == null)
                    m_View = new MyDSView(this, String.Empty);
	            return m_View;
	        }
	    }

	    public void TestFunction()
		{
			int n = 10;
			Console.WriteLine("test function");
			Console.WriteLine("n = {0}", n);
		}

		public MyDataSource()
		{
			
		}

        protected override ICollection GetViewNames()
        {
            return new string[1] {MyDSView.defaultName};
        }

        protected override DataSourceView GetView(string viewName)
        {
            return View;
        }
    }

    public class MyDSView : DataSourceView
    {
        public static readonly string defaultName = "MyDataSourceView";

        public MyDSView(IDataSource owner, string viewName) : base(owner, defaultName)
        {
        }

        protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
        {
            arguments.TotalRowCount = SampleDataHelper.RecordItems.Length;
            DataTable dt = new DataTable("DataTable");
            dt.Columns.Add("ID", RecordItem.IdType);
            dt.Columns.Add("RegistrationDate", RecordItem.RegistrationDateType);
            dt.Columns.Add("Name", RecordItem.NameType);
            foreach (RecordItem ri in SampleDataHelper.RecordItems)
            {
                DataRow dr = dt.NewRow();
                dr[0] = ri.Id;
                dr[1] = ri.RegistrationDate;
                dr[2] = ri.Name;
                dt.Rows.Add(dr);
            }
            DataView dv = dt.DefaultView;
            return dv;
        }

        public override bool CanRetrieveTotalRowCount
        {
            get
            {
                return true;
            }
        }
    }

    public struct RecordItem
    {
        private static readonly int m_MaxNameLength = 100;

        private static int m_IdCount = 0;

        private int m_Id;
        private DateTime m_RegistrationDate;
        private string m_Name;

        public RecordItem(DateTime registrationDate, string name)
        {
            m_Id = Interlocked.Increment(ref m_IdCount);
            m_RegistrationDate = registrationDate;
            m_Name = name;
        }

        public int Id
        {
            get { return m_Id; }
            set { m_Id = value; }
        }

        public DateTime RegistrationDate
        {
            get { return m_RegistrationDate; }
            set { m_RegistrationDate = value; }
        }

        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        static public Type IdType
        {
            get { return typeof(int); }
        }

        static public Type RegistrationDateType
        {
            get { return typeof(DateTime); }
        }

        static public Type NameType
        {
            get { return typeof(string); }
        }

        static public int MaxNameLength
        {
            get { return m_MaxNameLength; }
        }
    }

    public static class SampleDataHelper
    {
        private static RecordItem[] m_RecordItems;

        public static RecordItem[] RecordItems
        {
            get
            {
                if(m_RecordItems == null)
                {
                    m_RecordItems = new RecordItem[100];
                    for (int i = 0; i < m_RecordItems.Length; ++i)
                    {
                        m_RecordItems[i] = new RecordItem(DateTime.Today + new TimeSpan(1+i,0,0,0), "Name" + i);
                    }
                }
                return m_RecordItems;
            }
        }
    }

    public class RecordItemSchema : IDataSourceViewSchema
    {
        private readonly string m_Name = "RecordItem";

        public IDataSourceViewSchema[] GetChildren()
        {
            return null;
        }

        public IDataSourceFieldSchema[] GetFields()
        {
            IDataSourceFieldSchema[] res = new IDataSourceFieldSchema[]
                {
                    new IdFieldSchema(),
                    new RegDateFieldSchema(),
                    new NameFieldSchema()
                };
            return res;
        }

        public string Name
        {
            get { return m_Name; }
        }
    }

    public abstract class FieldSchemaBase : IDataSourceFieldSchema
    {
        public abstract Type DataType { get; }

        public bool Identity
        {
            get { return false; }
        }

        public bool IsReadOnly
        {
            get { return true; }
        }

        public bool Nullable
        {
            get { return false; }
        }

        public int Precision
        {
            get { return -1; }
        }

        public abstract bool IsUnique { get; }

        public abstract int Length { get; }

        public abstract string Name { get; }

        public abstract bool PrimaryKey { get; }

        public abstract int Scale { get; }
    }

    public class IdFieldSchema : FieldSchemaBase
    {
        override public Type DataType
        {
            get { return RecordItem.IdType; }
        }

        override public bool IsUnique
        {
            get { return true; }
        }

        override public int Length
        {
            get { return sizeof(int); }
        }

        override public string Name
        {
            get { return "ID"; }
        }

        override public bool PrimaryKey
        {
            get { return true; }
        }

        override public int Scale
        {
            get { return 10; }
        }
    }

    public class RegDateFieldSchema : FieldSchemaBase
    {
        public override Type DataType
        {
            get { return RecordItem.RegistrationDateType; }
        }

        public override bool IsUnique
        {
            get { return false; }
        }

        public override int Length
        {
            //we need sizeof(DateTime), but there is no way to obtain the size of DateTime. 
            //But DateTime internally based on uint
            //TODO: Maybe -1 is most suitable for non-string fields, we needs to check it
            get { return sizeof(uint); }
        }

        public override string Name
        {
            get { return "RegistrationDate"; }
        }

        public override bool PrimaryKey
        {
            get { return false; }
        }

        public override int Scale
        {
            get { return -1; }
        }
    }

    public class NameFieldSchema : FieldSchemaBase
    {
        public override Type DataType
        {
            get { return RecordItem.NameType; }
        }

        public override bool IsUnique
        {
            get { return false; }
        }

        public override int Length
        {
            get { return 100; }
        }

        public override string Name
        {
            get { return "Name"; }
        }

        public override bool PrimaryKey
        {
            get { return false; }
        }

        public override int Scale
        {
            get { return -1; }
        }
    }
}



/////////////////////////////////////////////

#region CvsDataSource

/// 
/// /////////////////////////////////////////

// The CsvDataSource is a data source control that retrieves its
// data from a comma-separated value file.
[AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Minimal)]
public class CsvDataSource : DataSourceControl
{
    public CsvDataSource() : base() {}

    // The comma-separated value file to retrieve data from.
    public string FileName {
        get {
            return ((CsvDataSourceView)this.GetView(String.Empty)).SourceFile;
        }
        set {
            // Only set if it is different.
            if ( ((CsvDataSourceView)this.GetView(String.Empty)).SourceFile != value) {
                ((CsvDataSourceView)this.GetView(String.Empty)).SourceFile = value;
                RaiseDataSourceChangedEvent(EventArgs.Empty);
            }
        }
    }

    // Do not add the column names as a data row. Infer columns if the CSV file does
    // not include column names.
    public bool IncludesColumnNames {
        get {
            return ((CsvDataSourceView)this.GetView(String.Empty)).IncludesColumnNames;
        }
        set {
            // Only set if it is different.
            if ( ((CsvDataSourceView)this.GetView(String.Empty)).IncludesColumnNames != value) {
                ((CsvDataSourceView)this.GetView(String.Empty)).IncludesColumnNames = value;
                RaiseDataSourceChangedEvent(EventArgs.Empty);
            }
        }
    }

    // Return a strongly typed view for the current data source control.
    private CsvDataSourceView view = null;
    protected override DataSourceView GetView(string viewName) {
        if (null == view) {
            view = new CsvDataSourceView(this, String.Empty);
        }
        return view;
    }
    // The ListSourceHelper class calls GetList, which
    // calls the DataSourceControl.GetViewNames method.
    // Override the original implementation to return
    // a collection of one element, the default view name.
    protected override ICollection GetViewNames() {
        ArrayList al = new ArrayList(1);
        al.Add(CsvDataSourceView.DefaultViewName);
        return al as ICollection;
    }
}

// The CsvDataSourceView class encapsulates the
// capabilities of the CsvDataSource data source control.
public class CsvDataSourceView : DataSourceView
{

    public CsvDataSourceView(IDataSource owner, string name) :base(owner, DefaultViewName) {

    }

    // The data source view is named. However, the CsvDataSource
    // only supports one view, so the name is ignored, and the
    // default name used instead.
    public static string DefaultViewName = "CommaSeparatedView";

    // The location of the .csv file.
    private string sourceFile = String.Empty;
    internal string SourceFile {
        get {
            return sourceFile;
        }
        set {
            // Use MapPath when the SourceFile is set, so that files local to the
            // current directory can be easily used.
            string mappedFileName = HttpContext.Current.Server.MapPath(value);
            sourceFile = mappedFileName;
        }
    }

    // Do not add the column names as a data row. Infer columns if the CSV file does
    // not include column names.
    private bool columns = false;
    internal bool IncludesColumnNames {
        get {
            return columns;
        }
        set {
            columns = value;
        }
    }

    // Get data from the underlying data source.
    // Build and return a DataView, regardless of mode.
    protected override IEnumerable ExecuteSelect(DataSourceSelectArguments selectArgs) {
        IEnumerable dataList = null;
        // Open the .csv file.
        if (File.Exists(this.SourceFile)) {
            DataTable data = new DataTable();

            // Open the file to read from.
            using (StreamReader sr = File.OpenText(this.SourceFile)) {
                // Parse the line
                string s = "";
                string[] dataValues;
                DataColumn col;

                // Do the following to add schema.
                dataValues = sr.ReadLine().Split(',');
                // For each token in the comma-delimited string, add a column
                // to the DataTable schema.
                foreach (string token in dataValues) {
                    col = new DataColumn(token,typeof(string));
                    data.Columns.Add(col);
                }

                // Do not add the first row as data if the CSV file includes column names.
                if (! IncludesColumnNames)
                    data.Rows.Add(CopyRowData(dataValues, data.NewRow()));

                // Do the following to add data.
                while ((s = sr.ReadLine()) != null) {
                    dataValues = s.Split(',');
                    data.Rows.Add(CopyRowData(dataValues, data.NewRow()));
                }
            }
            data.AcceptChanges();
            DataView dataView = new DataView(data);
            if (selectArgs.SortExpression != String.Empty) {
                dataView.Sort = selectArgs.SortExpression;
            }
            dataList = dataView;
        }
        else {
            throw new InvalidOperationException("File not found, " + this.SourceFile);
        }

        if (null == dataList) {
            throw new InvalidOperationException("No data loaded from data source.");
        }

        return dataList;
    }

    private DataRow CopyRowData(string[] source, DataRow target) {
        try {
            for (int i = 0;i < source.Length;i++) {
                target[i] = source[i];
            }
        }
        catch (System.IndexOutOfRangeException) {
            // There are more columns in this row than
            // the original schema allows.  Stop copying
            // and return the DataRow.
            return target;
        }
        return target;
    }
    // The CsvDataSourceView does not currently
    // permit deletion. You can modify or extend
    // this sample to do so.
    public override bool CanDelete {
        get {
            return false;
        }
    }
    protected override int ExecuteDelete(IDictionary keys, IDictionary values)
    {
        throw new NotSupportedException();
    }
    // The CsvDataSourceView does not currently
    // permit insertion of a new record. You can
    // modify or extend this sample to do so.
    public override bool CanInsert {
        get {
            return false;
        }
    }
    protected override int ExecuteInsert(IDictionary values)
    {
        throw new NotSupportedException();
    }
    // The CsvDataSourceView does not currently
    // permit update operations. You can modify or
    // extend this sample to do so.
    public override bool CanUpdate {
        get {
            return false;
        }
    }
    protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
    {
        throw new NotSupportedException();
    }
}

#endregion